package com.kehutong.payfees.service.record;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

import com.kehutong.common.util.Token;
import com.kehutong.payfees.service.ChargeBillServiceImpl;
import com.kehutong.payfees.service.ChargeTemporaryServiceImpl;
import com.kehutong.payfees.syn.CallBackNoReturnNoExeption;
import com.kehutong.payfees.syn.CallBackReturnNoException;
import com.kehutong.payfees.syn.Locks;
import org.coraframework.inject.Inject;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.http.Bean;
import org.coraframework.mvc.http.ReqMapping;
import org.coraframework.mvc.http.result.JSONResult;
import org.coraframework.mvc.http.result.Result;
import org.coraframework.mvc.webservice.WebService;
import org.coraframework.orm.Pool;
import org.coraframework.orm.jdbc.JdbcSession;

import com.kehutong.common.math.CalculatorUtils;
import com.kehutong.payfees.entity.ChargeBill;
import com.kehutong.payfees.entity.record.AccWater;
import com.kehutong.payfees.entity.record.Account;
import com.kehutong.payfees.entity.record.ChargeRecord;
import com.kehutong.payfees.enums.AccountType;
import com.kehutong.payfees.enums.WaterSource;
import org.coraframework.orm.util.Enums;

/**
 * 账号列表
 *
 * @author liuzhen
 * @email liuxing521a@kuwan123.com
 * @time 2018年3月8日下午1:58:33
 */
@WebService("/payfees/account/service")
public class AccoutServiceImpl {

	protected Logger logger = LoggerFactory.getLogger(getClass());
	
	@Inject
	private JdbcSession jdbcSession;

	@Inject
	private ChargeBillServiceImpl chargeBillService;

	@Inject
	private ChargeTemporaryServiceImpl chargeTemporaryService;

	/**
	 * 使用预存款缴费
	 * 房屋预存款 -> 物业钱包
	 * 先记录流水 再执行扣款增款
	 *
	 * @param houseNo     付款的房屋
	 * @param communityNo 收款的物业
	 * @throws Exception
	 */
	public JSONResult doPayWuYe(String orderNo, String houseNo, String communityNo, BigDecimal amt) {
		Account owner = getAccount(houseNo, AccountType.OWNER);
		if (owner.getUseAmt().compareTo(amt) < 0) {
			return Result.error(2001, "余额不足");
		}
	        
		CallBackNoReturnNoExeption called = ()->{
			Account community = getAccount(communityNo, AccountType.COMMUNITY);
			doDecWuyeWater(orderNo, owner, amt, WaterSource.PAY_BY_ADVANCE);
			doIncWuyeWater(orderNo, community, amt, WaterSource.PAY_BY_ADVANCE);
			owner.decUserAmt(amt);
			community.addUserAmt(amt);
	        	
			jdbcSession.updateById(owner);
			jdbcSession.updateById(community);
		};
		Locks.exeSyn(communityNo, called);
	        
		return Result.success();
	}

	/**
	 * 微信支付通知后
	 * 直接入账物业钱包
	 */
	public void doPayWuYe(ChargeBill bill) {
		final String communityNo = bill.getCommunityNo();
	        
		CallBackNoReturnNoExeption called = ()->{
			final BigDecimal amt = bill.getTotal();
			Account community = getAccount(communityNo, AccountType.COMMUNITY);
			doIncWuyeWater(bill.getId(), community, amt, WaterSource.WUYEFEI);
			community.addUserAmt(amt);

			jdbcSession.updateById(community);
		};
		Locks.exeSyn(communityNo, called);
	}

	/**
	 * 统一订单支付完成
	 * 向物业钱包打款
	 */
	public void doPayWuYe(ChargeRecord cr) {
		final String communityNo = cr.getCommunityNo();
	    	
		CallBackNoReturnNoExeption called = ()->{
			final BigDecimal amt = cr.getAmt();
			Account community = getAccount(communityNo, AccountType.COMMUNITY);
			doIncWuyeWater(cr.getId(), community, amt, cr.getBizType());
			community.addUserAmt(amt);

			jdbcSession.updateById(community);
		};
		Locks.exeSyn(communityNo, called);
	}

	/**
	 * 统一订单支付完成
	 * 向房屋钱包充值
	 */
	public void doChargeToHouse(ChargeRecord record) {
		final String houseNo = record.getHouseNo();
		CallBackNoReturnNoExeption called = ()->{
			Account account = getAccount(houseNo, AccountType.OWNER);
			doChargeRecord(record, account);
			account.addUserAmt(record.getAmt());
			jdbcSession.updateById(account);
		};
	        
		Locks.exeSyn(houseNo, called);
	}

	private void doChargeRecord(ChargeRecord cr, Account account) {
		AccWater accwater = Pool.newInstance(AccWater.class);
		accwater.setAccNo(account.getId());
		accwater.setOrderNo(cr.getId());
		accwater.setType(AccWater.INC_AMT);
		accwater.setSource(cr.getBizType());
		accwater.setAmt(cr.getAmt());
		accwater.setBeforeAmt(account.getUseAmt());
		accwater.setAfterAmt(account.getUseAmt().add(cr.getAmt()));

		jdbcSession.insert(accwater);
	}

	/**
	 * 记录出账流水
	 */
	private void doDecWuyeWater(String orderNo, Account account, BigDecimal amt, WaterSource source) {
		AccWater accwater = Pool.newInstance(AccWater.class);
		accwater.setAccNo(account.getId());
		accwater.setOrderNo(orderNo);
		accwater.setType(AccWater.DEC_AMT);
		accwater.setSource(source);
		accwater.setAmt(amt);
		accwater.setBeforeAmt(account.getUseAmt());
		accwater.setAfterAmt(account.getUseAmt().subtract(amt));

		jdbcSession.insert(accwater);
	}

	/**
	 * 自动记账入账流水
	 */
	private void doIncWuyeWater(String orderNo, Account account, BigDecimal amt, WaterSource source) {
		doIncWuyeWater(orderNo, account, amt, source, null);
	}

	/**
	 * @param orderNo 订单号
	 * @param account 账户
	 * @param amt     金额
	 * @param source  业务类型
	 * @param opCode  记账人编号(自动记账为空，手动记账非空)
	 */
	private void doIncWuyeWater(String orderNo, Account account, BigDecimal amt, WaterSource source, String opCode) {
		AccWater accwater = Pool.newInstance(AccWater.class);
		accwater.setAccNo(account.getId());
		accwater.setOrderNo(orderNo);
		accwater.setType(AccWater.INC_AMT);
		accwater.setSource(source);
		accwater.setAmt(amt);
		accwater.setBeforeAmt(account.getUseAmt());
		accwater.setAfterAmt(account.getUseAmt().add(amt));
		accwater.setOpCode(opCode);

		jdbcSession.insert(accwater);
	}

	private Account getAccount(String accountNo, AccountType type) {
		// double check sync
		CallBackReturnNoException<Account> called = ()->{
			Account account = Pool.get(Account.class, accountNo);
			if (Objects.nonNull(account)) {
				return account;
			}
	        	
			account = Pool.newInstance(Account.class, accountNo);
			account.setUseAmt(BigDecimal.ZERO);
			account.setType(type);
			jdbcSession.insert(account);
			return account;
		};
	        
		return (Account) Locks.exeSyn(accountNo, called);
	}

	/**
	 * 校验余额是否充足
	 */
	public boolean validateAccountAmtIsEnough(String accountNo, AccountType type, BigDecimal amt) {
		Account owner = getAccount(accountNo, type);
		return owner.getUseAmt().compareTo(amt) >= 0;
	}

	public Account getHouseAccount(String houseNo) {
		Account account = getAccount(houseNo, AccountType.OWNER);
		return account;
	}

	public BigDecimal sumChargeTotal(String houseNo) {
		List<ChargeRecord> chargeRecordList = jdbcSession.findArray(ChargeRecord.class)
				.eq("houseNo", houseNo)
				.eq("result", true)
				.in("bizType", WaterSource.CHONGZHI, WaterSource.CHARGE_BY_CASH).exe();

		BigDecimal totalCharge = chargeRecordList.stream()
				.map(ChargeRecord::getAmt)
				.reduce(BigDecimal.ZERO, BigDecimal::add);

		return CalculatorUtils.formatMoney(totalCharge);
	}

	/**
	* 现金充值，手动记账，增加房屋预存款
	*/
	public Object manualIncreHouseAccount(ChargeRecord cr, String opCode) {
		CallBackNoReturnNoExeption called = ()->{
			Account houseAccount = getHouseAccount(cr.getHouseNo());
			doIncWuyeWater(cr.getId(), houseAccount, cr.getAmt(), WaterSource.CHARGE_BY_CASH, opCode);
			houseAccount.addUserAmt(cr.getAmt());
                
			jdbcSession.updateById(houseAccount);
		};

		Locks.exeSyn(cr.getHouseNo(), called);

		return Result.success();
	}

}
